package com.beta.auth.controller;

import com.alibaba.fastjson.JSON;
import com.beta.auth.common.ResultVo;
import com.beta.auth.constants.AuthChannel;
import com.beta.auth.constants.BusinessConstants;
import com.beta.auth.dto.auth.AuthDto;
import com.beta.auth.dto.auth.BetaSdkDto;
import com.beta.auth.dto.auth.SDKDto;
import com.beta.auth.dto.auth.WXAuthState;
import com.beta.auth.service.AuthService;
import com.beta.auth.service.SelfAppAuthService;
import com.beta.auth.utils.ResponseUtils;
import com.beta.cat.service.RedissonService;
import com.beta.cat.utils.GuidUtil;
import com.beta.mq.redis.constant.TransCode;
import com.beta.mq.redis.producer.annotation.TransFlow;
import com.beta.user.auth.center.api.constant.UserAuthConstant;
import com.beta.user.auth.center.api.response.CommonResultEnum;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiOperationSupport;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;


/**
 * @author by zdwang
 * @Description
 * @Date 2021/7/30 16:25
 */
@Slf4j
@Api(value = "授权", tags = {"授权"})
@RestController
@RequestMapping("/wx")
public class AuthController implements InitializingBean {

    @Autowired
    private ApplicationContext ctx;

    @Autowired
    private RedissonService redissonService;

    @Autowired
    private SelfAppAuthService selfAppAuthService;

    private static final Map<String, AuthService> AUTH_SERVICES = new HashMap<>();

    /**
     * 获取授权链接
     *
     * @param authDto
     * @param request
     * @param response
     */
    @ApiOperation(value = "获取授权链接", notes = "获取授权链接")
    @ApiOperationSupport(order = 1)
    @RequestMapping("/Auth")
    @TransFlow(transCode = TransCode.WX_AUTH_TOKEN)
    public void auth(AuthDto authDto, HttpServletRequest request, HttpServletResponse response) {
        // 授权发起：生成授权请求参数缓存；授权回调：获取授权请求参数缓存
        WXAuthState authState = getWXAuthStateAsync(authDto);
        if (Objects.isNull(authState)) {
            log.warn("未获取到缓存的授权参数，入参：{}", JSON.toJSONString(authDto));
            ResponseUtils.toErrPage(BusinessConstants.SYSTEM_ERROR_MSG, BusinessConstants.SESSION_EXPIRED_CODE, response);
            return;
        }

        // 判断渠道是否合法，目前仅支持三种：企微自建应用授权、公众号授权、本地和SAAS联合授权
        String channel = authState.getAuthChannel();
        if (!AUTH_SERVICES.containsKey(channel)) {
            log.warn("非法的授权渠道，channel = {}", channel);
            ResponseUtils.toErrPage(BusinessConstants.SYSTEM_ERROR_MSG, BusinessConstants.PARAMS_INVALID_CODE, response);
            return;
        }

        // 进行授权
        AUTH_SERVICES.get(channel).oAuth(authDto, authState, request, response);
    }


    @ApiOperation(value = "sdk", notes = "sdk")
    @ApiOperationSupport(order = 2)
    @RequestMapping("/sdk")
    public ResultVo<BetaSdkDto> sdk(@RequestBody SDKDto dto) {
        BetaSdkDto betaSdkDto;
        try {
            if (Objects.isNull(dto) || StringUtils.isBlank(dto.getUrl())) {
                return ResultVo.failure(CommonResultEnum.PARAMETER_ERROR);
            }
            betaSdkDto = selfAppAuthService.getBetaSdkDto(dto.getUrl(), dto.getCorpId());
        } catch (Exception e) {
            log.error("URL Decode 失败，异常原因： ", e.getMessage(), e);
            return ResultVo.failure(CommonResultEnum.PARAMETER_URL_DECODE_FAIL);
        }
        return ResultVo.success(betaSdkDto);
    }


    @ApiOperation(value = "跳转错误页面", notes = "跳转错误页面")
    @ApiOperationSupport(order = 3)
    @RequestMapping("/Common/GetErrorPage")
    public void goErrorPage(@RequestParam(value = "errmsg") String errmsg, HttpServletResponse response) {
        try {
            ResponseUtils.toErrPage(errmsg, BusinessConstants.UNKNOWN_ERROR_CODE, response);
        } catch (Exception e) {
            log.error("AuthController goErrorPage 异常, 异常原因：", e.getMessage(), e);
        }
    }

    /**
     * 授权发起：生成授权请求参数缓存
     * 授权回调：获取授权请求参数缓存
     *
     * @param authDto
     * @return
     */
    private WXAuthState getWXAuthStateAsync(AuthDto authDto) {
        if (StringUtils.isBlank(authDto.getState())) {
            WXAuthState wxAuthState = new WXAuthState();

            // channel默认设为自建应用授权
            String channel = authDto.getAuthChannel();
            if (StringUtils.isEmpty(channel)) {
                channel = AuthChannel.SELF_APP.getChannel();
            }
            wxAuthState.setAuthChannel(channel);

            // 生成state
            String uuId = channel + "_" + GuidUtil.getUuid();
            wxAuthState.setReturnUrl(authDto.getReturnUrl());
            wxAuthState.setUstate(authDto.getUstate());
            wxAuthState.setState(uuId);
            wxAuthState.setExtAppId(authDto.getExtAppId());
            wxAuthState.setClientType(authDto.getClientType());
            wxAuthState.setWriteCookie(authDto.getWriteCookie());

            // scope默认设为基础授权
            String authType = authDto.getAuthType();
            if (StringUtils.isEmpty(authType)) {
                if (AuthChannel.SELF_APP.getChannel().equals(channel)) {
                    authType = UserAuthConstant.SELF_APP_AUTH_TYPE_BASE;
                } else if (AuthChannel.MP.getChannel().equals(channel)) {
                    authType = UserAuthConstant.MP_AUTH_TYPE_BASE;
                }
            }
            wxAuthState.setAuthType(authType);

            // 设置缓存
            redissonService.set(getAuthStateKey(uuId), JSON.toJSONString(wxAuthState), 1800000);
            return wxAuthState;
        }

        String authStateKey = getAuthStateKey(authDto.getState());
        WXAuthState wxAuthState = redissonService.get(authStateKey, WXAuthState.class);
        if (!Objects.isNull(wxAuthState) && StringUtils.isNotEmpty(wxAuthState.getState())) {
            return wxAuthState;
        }

        log.warn("获取缓存授权请求参数为空 rediskey={},wxAuthState={}", authStateKey, JSON.toJSONString(wxAuthState));
        return null;
    }

    /**
     * @return
     */
    private String getAuthStateKey(String state) {
        return "wxauth_" + state;
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        Map<String, AuthService> beans = ctx.getBeansOfType(AuthService.class);
        beans.forEach((k, v) -> {
            String channel = v.getChannel();
            if (AUTH_SERVICES.containsKey(channel)) {
                log.warn("存在渠道({channel={}})重复的授权服务实现类：{}、{}", channel, v.getClass().getCanonicalName(), v.getClass().getCanonicalName());
                throw new IllegalArgumentException("存在channel重复的授权服务实现类！");
            } else {
                AUTH_SERVICES.put(v.getChannel(), v);
            }
        });
    }
}
